// 3DTrackBallView.cpp : implementation file
//

#include "stdafx.h"
#include "TerrainEditor.h"
#include "TerrainEditorDoc.h"
#include "OpenGLView.h"
#include "3DArcBallView.h"
#include "OpenGLInfoDlg.h"
#include "MeshPropertiesDlg.h"
#include "glu.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// C3DArcBallView

IMPLEMENT_DYNCREATE(C3DArcBallView, COpenGLView)

C3DArcBallView::C3DArcBallView()
{
	m_bMouseLBDown = false;
	m_bWireframeMode = true;
	m_bShowTextures = false;
	m_bShowNormals = false;
	m_3DCam.m_vLensPos.Set(0.0f, 0.0f, 0.0f);
}

C3DArcBallView::~C3DArcBallView()
{
}

BEGIN_MESSAGE_MAP(C3DArcBallView, COpenGLView)
	//{{AFX_MSG_MAP(C3DArcBallView)
	ON_COMMAND(ID_MENUITEM_OpenGLInfo, OnMenuItemOpenGLInfo)
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_MOUSEMOVE()
	ON_WM_KEYDOWN()
	ON_COMMAND(ID_MENUITEM_MeshProperties, OnMenuItemMeshProperties)
	ON_COMMAND(ID_MENUITEM_WireframeMode, OnMenuItemWireframeMode)
	ON_COMMAND(ID_MENUITEM_ShowTextures, OnMenuItemShowTextures)
	ON_COMMAND(ID_MENUITEM_ShowNormals, OnMenuItemShowNormals)
	ON_UPDATE_COMMAND_UI(ID_MENUITEM_WireframeMode, OnUpdateMenuItemWireframeMode)
	ON_UPDATE_COMMAND_UI(ID_MENUITEM_ShowTextures, OnUpdateMenuItemShowTextures)
	ON_UPDATE_COMMAND_UI(ID_MENUITEM_ShowNormals, OnUpdateMenuItemShowNormals)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// C3DArcBallView drawing

/////////////////////////////////////////////////////////////////////////////
// C3DArcBallView diagnostics

#ifdef _DEBUG
void C3DArcBallView::AssertValid() const
{
	CView::AssertValid();
}

void C3DArcBallView::Dump(CDumpContext& dc) const
{
	CView::Dump(dc);
}

CTerrainEditorDoc* C3DArcBallView::GetDocument() // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CTerrainEditorDoc)));
	return (CTerrainEditorDoc*)m_pDocument;
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// C3DArcBallView message handlers

void C3DArcBallView::VirtualOnCreate()
{
	// Clear background color to black

	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

	// Reset Z-Buffer value

	glClearDepth(1.0f);
}

void C3DArcBallView::VirtualOnDraw()
{
	m_Timer.Timer(START);

	// Grab a pointer to the doc

	CTerrainEditorDoc *pDoc = GetDocument();

	// Check to see if a terrain has been created

	if (pDoc->m_bTerrainDataExists == true)
	{
		if (pDoc->m_b3DNewTerrainGenerated == true)
		{
			pDoc->m_TerrainMesh.CreateMesh(pDoc->m_Heightmap);
			pDoc->CreateTerrainTexture();
			pDoc->m_b3DNewTerrainGenerated = false;
			float fSize = (float)pDoc->m_Heightmap.m_HeightValues.m_pTGAFileHeader->Image.Width;
			float fMaxHeight = (float)pDoc->m_TerrainMesh.m_fMaxHeight;
			float fMeshSpacing = (float)pDoc->m_TerrainMesh.m_fMeshSpacing;
			m_3DCam.m_vCamPos.Set(0.0f, fMaxHeight * 0.5f, fSize * fMeshSpacing * 0.6f);
			m_3DCam.m_vLensPos.Set(0.0f, 0.0f, 0.0f);
		}

		glPushMatrix();

			// Process camera movement

			m_3DCam.MoveCam();
			
			// Draw terrain

			if (m_bWireframeMode)
			{
				glPolygonMode(GL_FRONT, GL_LINE);
				glPolygonMode(GL_BACK, GL_LINE);
				glCullFace(GL_BACK);
			}
			else
			{
				glPolygonMode(GL_FRONT, GL_FILL);
				glPolygonMode(GL_BACK, GL_FILL);
				glCullFace(GL_BACK);
			}

			pDoc->m_TerrainMesh.DrawMesh();

			if (m_bShowNormals) pDoc->m_TerrainMesh.DrawNormals();

		glPopMatrix();
	}

	m_Timer.Timer(STOP);

	CString sFPS;
	float fFPS = m_Timer.Timer(GETELAPSEDTIME);
	if (fFPS > 0.0f) sFPS.Format(_T("%.1f"), 1.0f / fFPS);
	else sFPS = "0.0";
	GetParent()->SetWindowText(GetDocument()->GetTitle() + ": 3DTrackBallView   [FPS: " + sFPS + "]");
}

void C3DArcBallView::VirtualOnSize(int width, int height)
{
	// Update member variables

	m_3DCam.m_ViewVol.m_nWndOriginX = 0;
	m_3DCam.m_ViewVol.m_nWndOriginY = 0;
	m_3DCam.m_ViewVol.m_nWndWidth = width;
	m_3DCam.m_ViewVol.m_nWndHeight = height;

	// Call view volume resize function

	m_3DCam.m_ViewVol.OnWindowResize();
}

void C3DArcBallView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) 
{

	// Adjust camera position based on mesh attributes

	CTerrainEditorDoc *pDoc = GetDocument();

	if (pDoc->m_bTerrainDataExists == true)
	{
		float fSize = (float)pDoc->m_Heightmap.m_HeightValues.m_pTGAFileHeader->Image.Width;
		float fMaxHeight = (float)pDoc->m_TerrainMesh.m_fMaxHeight;
		float fMeshSpacing = (float)pDoc->m_TerrainMesh.m_fMeshSpacing;
		m_3DCam.m_vCamPos.Set(0.0f, fMaxHeight * 0.5f, fSize * fMeshSpacing * 0.6f);
	}

	// Added here to allow different view classes to change the child window title

	CString sFPS;
	float fFPS = m_Timer.Timer(GETELAPSEDTIME);
	if (fFPS > 0.0f) sFPS.Format(_T("%.1f"), 1.0f / fFPS);
	else sFPS = "0.0";
	GetParent()->SetWindowText(GetDocument()->GetTitle() + ": 3DTrackBallView   [FPS: " + sFPS + "]");
	Invalidate(TRUE);
}

void C3DArcBallView::OnMenuItemOpenGLInfo() 
{
	COpenGLInfoDlg ogldlg;
	ogldlg.m_Company = "Company: " + m_extOpenGL.m_strCompany;
	ogldlg.m_Renderer = "Renderer: " + m_extOpenGL.m_strRenderer;
	ogldlg.m_Version = "Version: " + m_extOpenGL.m_strVersion;
	ogldlg.m_strExtensions = m_extOpenGL.m_strExtensions;
	ogldlg.m_strWGLExtensions = m_extOpenGL.m_strWGLExtensions;
	ogldlg.DoModal();	
}

void C3DArcBallView::OnLButtonDown(UINT nFlags, CPoint point) 
{
	// Mouse LB is down

	m_bMouseLBDown = true;
	m_vMouseFromPos.x = (2.0f * (float)point.x - m_3DCam.m_ViewVol.m_nWndWidth) / m_3DCam.m_ViewVol.m_nWndWidth;
	m_vMouseFromPos.y = (m_3DCam.m_ViewVol.m_nWndHeight - 2.0f * (float)point.y) / m_3DCam.m_ViewVol.m_nWndHeight;

	// Change to new cursor

	m_hMouseCursor = AfxGetApp()->LoadStandardCursor(IDC_SIZEALL);
	SetCursor(m_hMouseCursor);

	// Causes all subsequent mouse input to be sent to the current 
	// CWnd object regardless of the position of the cursor. 

	SetCapture(); // remember to release!
}

void C3DArcBallView::OnLButtonUp(UINT nFlags, CPoint point) 
{
	// Mouse LB is up

	m_bMouseLBDown = false;
	m_vMouseFromPos.x = m_vMouseFromPos.y = 0.0f;

	// Restore normal cursor

	m_hMouseCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
	SetCursor(m_hMouseCursor);

	// Other windows can now receive mouse input

	ReleaseCapture();
}

void C3DArcBallView::OnMouseMove(UINT nFlags, CPoint point) 
{
	if (m_bMouseLBDown) // basic move map operation
	{
		m_vMouseToPos.x = (2.0f * (float)point.x - m_3DCam.m_ViewVol.m_nWndWidth) / m_3DCam.m_ViewVol.m_nWndWidth;
		m_vMouseToPos.y = (m_3DCam.m_ViewVol.m_nWndHeight - 2.0f * (float)point.y) / m_3DCam.m_ViewVol.m_nWndHeight;
		m_3DCam.ApplyQuaternionRotationFromMouse(m_vMouseFromPos, m_vMouseToPos);
		m_vMouseFromPos = m_vMouseToPos;
		Invalidate(TRUE);
	}

	// Display the camera position in the status pane

	CString sMousePosition;
	sMousePosition.Format("[Camera Position: x=%.1f/y=%.1f/z=%.1f]", m_3DCam.m_vCamPos.x, m_3DCam.m_vCamPos.y, m_3DCam.m_vCamPos.z);
	CTerrainEditorApp *pApp = (CTerrainEditorApp*)AfxGetApp();
	pApp->m_pMainFrame->m_wndStatusBar.SetPaneText(0, sMousePosition, TRUE);

}

void C3DArcBallView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	// Zoom camera in and out

	switch(nChar)
	{
		case 90: // Z
			m_3DCam.ZoomIn();
			break;
		case 88: // X
			m_3DCam.ZoomOut();
			break;
	}

	// Use the keypad to rotate the view

	float fRotationAngle = DEGTORAD(1.0f); // 1 degree rotations
	m_3DCam.ApplyQuaternionRotationFromKeypad(nChar, fRotationAngle);

	COpenGLView::OnKeyDown(nChar, nRepCnt, nFlags);
	Invalidate(TRUE);

	// Display the camera position in the status pane

	CString sMousePosition;
	sMousePosition.Format("[Camera Position: x=%.1f/y=%.1f/z=%.1f]", m_3DCam.m_vCamPos.x, m_3DCam.m_vCamPos.y, m_3DCam.m_vCamPos.z);
	CTerrainEditorApp *pApp = (CTerrainEditorApp*)AfxGetApp();
	pApp->m_pMainFrame->m_wndStatusBar.SetPaneText(0, sMousePosition, TRUE);

}

void C3DArcBallView::OnMenuItemMeshProperties() 
{
	CTerrainEditorDoc *pDoc = GetDocument();

	if (pDoc->m_bTerrainDataExists == true)
	{
		CMeshPropertiesDlg mpdlg(this, GetDocument());
		mpdlg.DoModal();
	}
	else
	{
		AfxMessageBox("No image data is available to edit.");
	}
}

void C3DArcBallView::OnMenuItemWireframeMode() 
{
	m_bWireframeMode = !m_bWireframeMode;
	Invalidate(TRUE);
}

void C3DArcBallView::OnUpdateMenuItemWireframeMode(CCmdUI* pCmdUI) 
{
	pCmdUI->SetCheck((int)m_bWireframeMode);
}

void C3DArcBallView::OnMenuItemShowTextures() 
{
	m_bShowTextures = !m_bShowTextures;
	Invalidate(TRUE);
}

void C3DArcBallView::OnUpdateMenuItemShowTextures(CCmdUI* pCmdUI) 
{
	pCmdUI->SetCheck((int)m_bShowTextures);
}

void C3DArcBallView::OnMenuItemShowNormals() 
{
	m_bShowNormals = !m_bShowNormals;
	Invalidate(TRUE);
}

void C3DArcBallView::OnUpdateMenuItemShowNormals(CCmdUI* pCmdUI) 
{
	pCmdUI->SetCheck((int)m_bShowNormals);
}
